package com.quanxiaoha.xiaohashu.auth.service.impl;

import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpUtil;
import com.google.common.base.Preconditions;
import com.quanxiaoha.framework.biz.context.holder.LoginUserContextHolder;
import com.quanxiaoha.framework.common.exception.BizException;
import com.quanxiaoha.framework.common.response.Response;
import com.quanxiaoha.framework.common.util.JsonUtils;
import com.quanxiaoha.framework.redis.constant.RedisKeyConstants;
import com.quanxiaoha.xiaohashu.auth.enums.LoginTypeEnum;
import com.quanxiaoha.xiaohashu.auth.enums.ResponseCodeEnum;
import com.quanxiaoha.xiaohashu.auth.model.vo.user.UpdatePasswordReqVO;
import com.quanxiaoha.xiaohashu.auth.model.vo.user.UserLoginReqVO;
import com.quanxiaoha.xiaohashu.auth.rpc.UserRpcService;
import com.quanxiaoha.xiaohashu.auth.service.AuthService;
import com.quanxiaoha.xiaohashu.user.dto.resp.FindUserByPhoneRspDTO;
import jakarta.annotation.Resource;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import java.util.Objects;

/**
 * @author jingyanliu
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class AuthServiceImpl implements AuthService {
	private final PasswordEncoder passwordEncoder;
	@Resource
	private ThreadPoolTaskExecutor taskExecutor;
	@Resource
	private RedisTemplate<String, String> redisTemplate;
	@Resource
	private UserRpcService userRpcService;
	
	/**
	 * 登录与注册
	 */
	@Override
	public Response<String> loginAndRegister(UserLoginReqVO userLoginReqVO) {
		String phone = userLoginReqVO.getPhone();
		Integer type = userLoginReqVO.getType();
		
		LoginTypeEnum loginTypeEnum = LoginTypeEnum.valueOf(type);
		// 登录类型错误
		if (Objects.isNull(loginTypeEnum)) {
			throw new BizException(ResponseCodeEnum.LOGIN_TYPE_ERROR);
		}
		Long userId = null;
		FindUserByPhoneRspDTO findUserByPhoneRspDTO;
		// 判断登录类型
		switch (loginTypeEnum) {
			// 验证码登录
			case VERIFICATION_CODE -> {
				String verificationCode = userLoginReqVO.getCode();
				
				// 校验入参验证码是否为空
				Preconditions.checkArgument(StringUtils.isNotBlank(verificationCode), "验证码不能为空");
				
				// 构建验证码 Redis Key
				String key = RedisKeyConstants.buildVerificationCodeKey(phone);
				// 查询存储在 Redis 中该用户的登录验证码
				String sentCode = redisTemplate.opsForValue().get(key);
				
				// 判断用户提交的验证码，与 Redis 中的验证码是否一致
				if (!StringUtils.equals(verificationCode, sentCode)) {
					throw new BizException(ResponseCodeEnum.VERIFICATION_CODE_ERROR);
				}
				
				// 通过手机号查询记录
				findUserByPhoneRspDTO = userRpcService.findUserByPhone(phone);
				
				log.info("==> 用户是否注册, phone: {}, findUserByPhoneRspDTO: {}", phone, JsonUtils.toJsonString(findUserByPhoneRspDTO));
				
				// 判断是否注册
				if (Objects.isNull(findUserByPhoneRspDTO)) {
					// 若此用户还没有注册，系统自动注册该用户
					userId = registerUser(phone);
					
				} else {
					// 已注册，则获取其用户 ID
					userId = findUserByPhoneRspDTO.getId();
				}
			}
			
			case PASSWORD -> {
				//密码登录
				String password = userLoginReqVO.getPassword();
				
				// 根据手机号查询
				findUserByPhoneRspDTO = userRpcService.findUserByPhone(phone);
				
				// 判断该手机号是否注册
				if (Objects.isNull(findUserByPhoneRspDTO)) {
					throw new BizException(ResponseCodeEnum.USER_NOT_FOUND);
				}
				
				// 拿到密文密码
				String encodePassword = findUserByPhoneRspDTO.getPassword();
				
				// 匹配密码是否一致
				boolean isPasswordCorrect = passwordEncoder.matches(password, encodePassword);
				
				// 如果不正确，则抛出业务异常，提示用户名或者密码不正确
				if (!isPasswordCorrect) {
					throw new BizException(ResponseCodeEnum.PHONE_OR_PASSWORD_ERROR);
				}
				
				userId = findUserByPhoneRspDTO.getId();
			}
		}
		// SaToken 登录用户
		StpUtil.login(userId);
		// 获取 Token 令牌
		SaTokenInfo tokenInfo = StpUtil.getTokenInfo();
		// 返回 Token 令牌
		return Response.success(tokenInfo.tokenValue);
	}
	
	/**
	 * 退出登录
	 */
	@Override
	public Response<?> logout() {
		Long userId = LoginUserContextHolder.getUserId();
		log.info("==> 用户退出登录, userId: {}", userId);
		taskExecutor.execute(() -> {
			// 异步删除 Redis 中的用户角色信息
			String userRolesKey = RedisKeyConstants.buildUserRoleKey(userId);
			redisTemplate.delete(userRolesKey);
		});
		StpUtil.logout(userId);
		return Response.success();
	}
	
	@Override
	public Response<?> updatePassword(UpdatePasswordReqVO updatePasswordReqVO) {
		// 新密码
		String newPassword = updatePasswordReqVO.getNewPassword();
		// 密码加密
		String encodePassword = passwordEncoder.encode(newPassword);
		
		// 调用 RPC 服务，更新密码
		userRpcService.updatePassword(encodePassword);
		return Response.success();
	}
	
	@Override
	public Long registerUser(String phone) {
		// RPC: 调用用户服务，注册用户
		Long userIdTmp = userRpcService.registerUser(phone);
		
		// 若调用用户服务，返回的用户 ID 为空，则提示登录失败
		if (Objects.isNull(userIdTmp)) {
			throw new BizException(ResponseCodeEnum.LOGIN_FAIL);
		}
		
		return userIdTmp;
	}
}




